package cn.hjh.rpc.define;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AbstractExchange;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.config.BindingFactoryBean;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import cn.hjh.rpc.common.RPCContants;

/**
 * spring bean 注册抽象类
 * @author jianhua.huang
 * */
public abstract class AbstractMQBeanRegister {
	
	private Logger logger = LoggerFactory.getLogger(getClass());

	protected RabbitAdmin admin;

	protected BeanDefinitionRegistry beanRegistry;

	protected ConfigurableListableBeanFactory beanFactory;
	
	protected String connFactoryRef;
	
	protected void init(String connFactoryRef, ConfigurableListableBeanFactory configurableListableBeanFactory,BeanDefinitionRegistry registry)
	{
		if (beanFactory == null) {
			logger.debug("初始化MQ注册器.");
			this.beanFactory = configurableListableBeanFactory;
			this.beanRegistry = registry;
		}
		if(StringUtils.isBlank(connFactoryRef))
		{
			connFactoryRef = RPCContants.CONNECTION_FACTORY_BEAN_ID;
		}
		String adminBeanName = connFactoryRef + RPCContants.ADM_BEANID_SUFFIX;
		this.connFactoryRef = connFactoryRef;
		if(!beanFactory.containsBean(adminBeanName))
		{
			logger.info("admin 为 ：{} 尚未注册到spring中，现在开始注册流程", adminBeanName);
			BeanDefinitionBuilder adminBuilder = BeanDefinitionBuilder.genericBeanDefinition(RabbitAdmin.class);
			adminBuilder.addConstructorArgReference(connFactoryRef);
			BeanDefinition adminDefinition = adminBuilder.getBeanDefinition();
			BeanDefinitionReaderUtils.registerBeanDefinition(
					new BeanDefinitionHolder(adminDefinition, adminBeanName), beanRegistry);
		}
		else
		{
			logger.info("admin 为 ：{} 已经注册到spring中，现在开始注册流程", adminBeanName);
		}
		// 根据名字获取，业务系统可另外定义admin
		this.admin = beanFactory.getBean(adminBeanName, RabbitAdmin.class);

	}
	
	/**
	 * @param exChangeName
	 */
	protected String declareDirectExchange(String exChangeName, String durable, String autoDelete) {
		// 创建directExchange
		return declareExchange(DirectExchange.class, exChangeName, durable, autoDelete);
	}

	protected String declareTopicExchange(String exChangeName, String durable, String autoDelete) {
		// 创建topicExchange
		return declareExchange(TopicExchange.class, exChangeName, durable, autoDelete);
	}

	protected String declareFanoutExchange(String exChangeName, String durable, String autoDelete) {
		// 创建fanoutExchange
		return declareExchange(FanoutExchange.class, exChangeName, durable, autoDelete);
	}

	protected String declareExchange(Class<? extends Exchange> clazz, String exChangeName, String durable,
			String autoDelete) {
		// 创建directExchange
		BeanDefinitionBuilder directExchangeBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
		directExchangeBuilder.addConstructorArgValue(new TypedStringValue(exChangeName));
		directExchangeBuilder.addConstructorArgValue(new TypedStringValue(durable));
		directExchangeBuilder.addConstructorArgValue(new TypedStringValue(autoDelete));
		BeanDefinition directExchangeBeanDefinition = directExchangeBuilder.getBeanDefinition();
		String defaultDirectExchangeId = BeanDefinitionReaderUtils.generateBeanName(directExchangeBeanDefinition,
				beanRegistry);
		BeanDefinitionReaderUtils.registerBeanDefinition(
				new BeanDefinitionHolder(directExchangeBeanDefinition, defaultDirectExchangeId), beanRegistry);
		AbstractExchange exchange = (AbstractExchange) beanFactory.getBean(defaultDirectExchangeId);
		exchange.setShouldDeclare(false);
		admin.declareExchange(exchange);
		return defaultDirectExchangeId;
	}

	/**
	 * @param queueName
	 */
	protected String declareQueue(String queueName, String durable, String exclusive, String autoDelete) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Queue.class);
		builder.addConstructorArgValue(new TypedStringValue(queueName));
		builder.addConstructorArgValue(new TypedStringValue(durable));
		builder.addConstructorArgValue(new TypedStringValue(exclusive));
		builder.addConstructorArgValue(new TypedStringValue(autoDelete));
		BeanDefinition queueBeanDefinition = builder.getBeanDefinition();
		String queueId = BeanDefinitionReaderUtils.generateBeanName(queueBeanDefinition, beanRegistry);
		BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(queueBeanDefinition, queueId),
				beanRegistry);
		Queue queue = (Queue) beanFactory.getBean(queueId);
		queue.setShouldDeclare(false);
		admin.declareQueue(queue);
		return queueId;
	}

	/**
	 * 绑定Queue到Exchange上
	 * 
	 * @param exchangeName
	 * @param routingKey
	 * @param queueId
	 */
	protected String declareBinding(String exchangeName, String routingKey, String queueId) {
		BeanDefinitionBuilder bindingBuilder = BeanDefinitionBuilder.genericBeanDefinition(BindingFactoryBean.class);
		bindingBuilder.addPropertyValue("exchange", new TypedStringValue(exchangeName));
		bindingBuilder.addPropertyValue("routingKey", new TypedStringValue(routingKey));
		bindingBuilder.addPropertyReference("destinationQueue", queueId);
		BeanDefinition bingingBeanDefinition = bindingBuilder.getBeanDefinition();
		String bingingId = BeanDefinitionReaderUtils.generateBeanName(bingingBeanDefinition, beanRegistry);
		BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(bingingBeanDefinition, bingingId),
				beanRegistry);
		Binding binding = (Binding) beanFactory.getBean(bingingId);
		binding.setShouldDeclare(false);
		admin.declareBinding(binding);
		return bingingId;
	}
	
	/**
	 * 声明一个发送模板bean
	 * 
	 * */
	protected String declareAmqpTemplate(RPCClientBean bean) 
	{
		BeanDefinitionBuilder bindingBuilder = BeanDefinitionBuilder.genericBeanDefinition(RabbitTemplate.class);
		bindingBuilder.addPropertyValue("exchange", new TypedStringValue(bean.getExchangeName()));
		bindingBuilder.addPropertyReference(RPCContants.CONNECTION_FACTORY, connFactoryRef);
		bindingBuilder.addPropertyValue("replyTimeout", bean.getReplyTimeout());
		
		bindingBuilder.addPropertyValue("encoding", RPCContants.DEFAULT_ENCODE);
		BeanDefinition bingingBeanDefinition = bindingBuilder.getBeanDefinition();
//		String bingingId = BeanDefinitionReaderUtils.generateBeanName(bingingBeanDefinition, beanRegistry);
		String beanId = bean.getSendQueueName();
		BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(bingingBeanDefinition, beanId),
				beanRegistry);
		RabbitTemplate rabbitTemplate = (RabbitTemplate) beanFactory.getBean(beanId);
		rabbitTemplate.setReplyAddress(bean.getReplyQueueName());
		return beanId;
	}
	
}
